package com.tools.common.container;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.tools.common.object.*;
import com.tools.common.object.string.VerifyKit;
import com.tools.common.object.json.JsonKit;

import java.util.*;

/**
 * JSON 字符串的
 * 实体，内部封装一个
 * Map 集合，可用于
 * 网络间传输数据，或者
 * 作为后端程序各个层、服务
 * 直接传输数据的 DTO
 * */
@Note("JSON 字符串的实体，内部封装一个Map 集合，可用于网络间传输数据，或者" +
        "作为后端程序各个层、服务直接传输数据的 DTO")
public final class JsonPair extends AbsOwnPair<String, Object> {

    @Note("自定义响应状态码属性")
    private int code = -1;

    @Note("自定义响应状态描述信息")
    private String message = "";

    @Note("如果响应的数据只有 data 一个字段，则不需要初始化父类 AbsOwnPair 内置的 LinkedHashMap 集合")
    private Object data;

    @Note("code 属性的名称")
    public static final String CODE = "code";
    @Note("message 属性的名称")
    public static final String MESSAGE = "message";
    @Note("data 属性的名称")
    public static final String DATA = "data";

    public JsonPair() {
        super(false, MapEnum.LINKED);
    }

    public JsonPair(Integer code, String message) {
        super(false, MapEnum.LINKED);
        this.setCode(code);
        this.setMessage(message);
    }

    public JsonPair(Integer code, String message, Object data) {
        super(false, MapEnum.LINKED);
        this.setCode(code);
        this.setMessage(message);
        this.data = data;
    }

    /* *******************************************************************************************
     *
     *          Getter / Setter
     *
     * *******************************************************************************************
     * */

    public int getCode() {
        return code;
    }

    public void setCode(Integer code) {
        if(code == null) throw new NullPointerException("不能设置 null 的回应编码");
        this.code = code;
        if(super.initialized()) {
            super.protected_putOne(CODE, this.code);
        }
    }

    public void setCode(int code) {
        this.code = code;
        if(super.initialized()) {
            super.protected_putOne(CODE, this.code);
        }
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = ObjKit.toStringEmptyString(message);
        if(super.initialized()) {
            super.protected_putOne(MESSAGE, this.message);
        }
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
        if(super.initialized()) {
            super.protected_putOne(DATA, this.data);
        }
    }

    public JsonPair code(Integer code) {
        this.setCode(code);
        return this;
    }

    public JsonPair code(int code) {
        this.setCode(code);
        return this;
    }

    public JsonPair message(String message) {
        this.setMessage(message);
        return this;
    }

    public JsonPair data(Object data) {
        this.setData(data);
        return this;
    }

    /* *******************************************************************************************
     *
     *          继承的方法重写
     *
     * *******************************************************************************************
     * */

    public String toJSON() {
        return JsonKit.toJSON(this);
    }

    @Note("保留值为 null 的字段")
    public String asJSON() {
        return JsonKit.asJSON(this);
    }

    @Override
    public String toString() {
        return super.initialized()
                ? "JsonPair" + super.toString()
                : "JsonPair{" +
                        "code=" + code +
                        ", message=" + message +
                        ", data=" + data +
                    '}';
    }

    @Override
    public boolean equals(Object o) {
        if(this == o) return true;
        if(o instanceof JsonPair) {
            JsonPair jsonPair = (JsonPair) o;
            if(jsonPair.initialized()) return super.equals(jsonPair);
            if(data == null) return Objects.equals(code, jsonPair.code) && Objects.equals(message, jsonPair.message);
            return Objects.equals(data, jsonPair.data);
        }
        return false;
    }

    @Override
    public int hashCode() {
        if(super.initialized()) return super.hashCode();
        if(data == null) Objects.hash(code, message);
        return Objects.hash(code, message, data);
    }

    @Override
    public JsonPair put(String name, Object value) {
        private_putOne(name, value);
        return this;
    }

    @Override
    public JsonPair putIfAbsent(String sourceKey, Object backupValue) {
        if(CODE.equals(sourceKey) || MESSAGE.equals(sourceKey)) {
            return this;
        }
        if(DATA.equals(sourceKey) && data != null) {
            this.setData(backupValue);
            return this;
        }
        if(super.initialized()) {
            super.protected_putIfAbsent(sourceKey, backupValue);
            return this;
        }
        private_lazyInitBody(1);
        super.protected_putOne(sourceKey, backupValue);
        return this;
    }

    @Override
    public JsonPair putAll(Map map) {
        if(map == null || map.isEmpty()) return this;
        private_lazyInitBody(map.size());
        for (Object o : map.entrySet()) {
            Map.Entry e = (Map.Entry) o;
            String key = ObjKit.asStringValue(e.getKey(), null);
            Object value = e.getValue();
            if(private_addCheck(key, value)) continue;
            super.protected_putOne(key, value);
        }
        return this;
    }

    @Override
    public JsonPair putAll(OwnPairs pairs) {
        if(pairs == null || pairs.isEmpty()) return this;
        private_lazyInitBody(pairs.size());
        for (Object o : pairs.entrySet()) {
            Map.Entry e = (Map.Entry) o;
            Object value = e.getValue();
            String key = ObjKit.asStringValue(e.getKey(), null);
            if(private_addCheck(key, value)) continue;
            super.protected_putOne(key, value);
        }
        return this;
    }

    public JsonPair putAll(String jsonObject) {
        if(!VerifyKit.isJsonObject(jsonObject)) return this;
        JsonNode jsonNode = JsonKit.readToNode(jsonObject);
        int size = jsonNode.size();
        if(size == 0) return this;
        private_lazyInitBody(size);
        Iterator<Map.Entry<String, JsonNode>> fields = jsonNode.fields();
        while (fields.hasNext()) {
            Map.Entry<String, JsonNode> e = fields.next();
            String name = e.getKey();
            Object value = ObjKit.stringToRealValue(ObjKit.asStringValue(e.getValue(), null), true);
            if(private_addCheck(name, value)) continue;
            super.protected_putOne(name, value);
        }
        return this;
    }


    public JsonPair putAll(Object bean) {
        if(bean == null) return this;
        Map<String, Object> valuesMap = ObjKit.allFieldValue(bean);
        for (Map.Entry<String, Object> e : valuesMap.entrySet()) {
            String name = e.getKey();
            Object fieldValue = e.getValue();
            if(private_addCheck(name, fieldValue)) continue;
            super.protected_putOne(name, fieldValue);
        }
        return this;
    }


    @Override
    public JsonPair remove(Object key) {
        if(private_removeCheck(key)) return this;
        super.protected_removeOne(key);
        return this;
    }

    @Override
    public JsonPair remove(String key, Object value) {
        if(private_removeCheck(key)) return this;
        super.protected_removeOne(key, value);
        return this;
    }


    @Override
    public JsonPair removeAll(Object... names) {
        if(names == null || names.length == 0) return this;
        for (Object name : names) {
            if(private_removeCheck(name)) continue;
            super.protected_removeOne(name);
        }
        return this;
    }

    @Override
    public JsonPair removeAll(Collection<?> names) {
        if(names == null || names.isEmpty()) return this;
        for(Object name : names) {
            if(private_removeCheck(name)) continue;
            super.protected_removeOne(name);
        }
        return this;
    }

    @Override
    public JsonPair removeAll(OwnRows<?> names) {
        if(names == null || names.isEmpty()) return this;
        for(Object name : names) {
            if(private_removeCheck(name)) continue;
            super.protected_removeOne(name);
        }
        return this;
    }

    @Override
    public JsonPair removeAll(Iterable<?> names) {
        if(names != null) {
            for(Object name : names) {
                if(private_removeCheck(name)) continue;
                super.protected_removeOne(name);
            }
        }
        return this;
    }


    @Override
    public JsonPair removeAll(Map<? extends String, ?> removes) {
        if(removes != null && !removes.isEmpty()) {
            for(Map.Entry<? extends String, ?> e : removes.entrySet()) {
                String name = e.getKey();
                Object value = e.getValue();
                if(private_removeCheck(name, value)) continue;
                super.protected_removeOne(name, value);
            }
        }
        return this;
    }


    @Override
    public JsonPair removeAll(OwnPairs<? extends String, ?> removes) {
        if(removes == null || removes.isEmpty()) return this;
        for(Map.Entry<? extends String, ?> e : removes.entrySet()) {
            Object value = e.getValue();
            String name = e.getKey();
            if(private_removeCheck(name, value)) continue;
            super.protected_removeOne(name, value);
        }
        return this;
    }

    public JsonPair removeAll(String jsonObject) {
        if(VerifyKit.isJsonObject(jsonObject)) {
            JsonNode jsonNode = JsonKit.readToNode(jsonObject);
            if(jsonNode.isEmpty()) return this;
            Iterator<Map.Entry<String, JsonNode>> fields = jsonNode.fields();
            while (fields.hasNext()) {
                Map.Entry<String, JsonNode> e = fields.next();
                String name = e.getKey();
                Object value = ObjKit.stringToRealValue(ObjKit.asStringValue(e.getValue(), null), true);
                if(private_removeCheck(name, value)) continue;
                super.protected_removeOne(name, value);
            }
        }
        return this;
    }


    public JsonPair removeAll(Object javaBean) {
        if(javaBean == null) return this;
        Map<String, Object> fieldMap = ObjKit.allFieldValue(javaBean.getClass());
        return this.removeAll(fieldMap);
    }

    @Override
    public JsonPair replace(String name, Object newValue) {
        if(private_addCheck(name, newValue)) return this;
        super.protected_replace(name, newValue);
        return this;
    }

    @Override
    public JsonPair replace(String name, Object oldValue, Object newValue) {
        if(CODE.equals(name) && BasicEnum.INT.eq(code, oldValue)) {
            this.setCode(BasicEnum.intValue(newValue));
            return this;
        }
        if(MESSAGE.equals(name) && Objects.equals(message, ObjKit.asStringValue(oldValue, null))) {
            this.setMessage(ObjKit.toStringEmptyString(newValue));
            return this;
        }
        if(DATA.equals(name) && Objects.equals(data, oldValue)) {
            this.setData(newValue);
            return this;
        }
        if(super.initialized() && !super.isEmpty()) {
            super.protected_replace(name, oldValue, newValue);
        }
        return this;
    }

    @Override
    public Object get(Object name) {
        if(CODE.equals(name)) return code;
        if(MESSAGE.equals(name)) return message;
        if(DATA.equals(name)) return data;
        return super.get(name);
    }


    public <T>T get(String name, Class<T> valueType) {
        return (T) get(name);
    }

    public <T>T get(String name, C<T> valueType) {
        return (T) ObjKit.toRealValue(get(name), valueType);
    }

    @Override
    public Object getOrDefault(Object name, Object defaultValue) {
        if(CODE.equals(name)) return code;
        if(MESSAGE.equals(name)) return message;
        if(DATA.equals(name)) return data;
        return super.getOrDefault(name, defaultValue);
    }


    @Override
    public Set<String> keySet() {
        if(super.initialized()) {
            return super.keySet();
        }
        Set<String> keySet = SetEnum.HASH.newSet(3);
        keySet.add(CODE);
        keySet.add(MESSAGE);
        if(data != null) keySet.add(DATA);
        return keySet;
    }

    @Override
    public Set<Map.Entry<String, Object>> entrySet() {
        if(super.initialized()) {
            return super.entrySet();
        }
        Set<Map.Entry<String, Object>> entrySet = SetEnum.HASH.newSet(2);
        entrySet.add(new MapEntry<>(CODE, this.code));
        entrySet.add(new MapEntry<>(MESSAGE, this.message));
        if(data != null) entrySet.add(new MapEntry<>(DATA, this.data));
        return entrySet;
    }

    @Override
    public Collection<Object> values() {
        if(super.initialized()) {
            return super.values();
        }
        ArrayList<Object> values = new ArrayList<>(3);
        values.add(this.code);
        values.add(this.message);
        if(data != null) values.add(this.data);
        return values;
    }

    @Override
    public int size() {
        return super.initialized() ? super.size() : 0;
    }

    @Override
    public void clear() {
        if(data != null) this.data = null;
        if(super.initialized()) {
            super.clear();
        }
    }

    @Override
    public Object getOrDefaultAndPut(Object key, Object defaultValue) {
        if(CODE.equals(key)) return code;
        if(MESSAGE.equals(key)) return message;
        if(DATA.equals(key)) return (data == null) ? defaultValue : data;
        return super.getOrDefaultAndPut(key, defaultValue);
    }

    @Override
    public boolean containsKey(String name) {
        if(CODE.equals(name) || MESSAGE.equals(name)) return true;
        if(DATA.equals(name)) return data != null;
        return super.containsKey(name);
    }

    @Override
    public boolean containsAllKey(String... names) {
        if(names == null || names.length == 0) return false;
        if(!super.initialized() || super.isEmpty()) return false;
        for (String name : names) {
            if(!containsKey(name)) return false;
        }
        return true;
    }

    @Override
    public boolean containsAllKey(Collection<? extends String> names) {
        if(names == null || names.isEmpty()) return false;
        if(!super.initialized() || super.isEmpty()) return false;
        for (String name : names) {
            if(!containsKey(name)) return false;
        }
        return true;
    }


    @Override
    public boolean containsAllKey(OwnRows<? extends String> names) {
        if(names == null || names.isEmpty()) return false;
        if(super.initialized() && !super.isEmpty()) {
            for (String name : names) {
                if(!containsKey(name)) return false;
            }
            return true;
        }
        return false;
    }


    @Override
    public boolean containsAllKey(Iterable<? extends String> names) {
        if(names == null) return false;
        Iterator<? extends String> iterator = names.iterator();
        if(!iterator.hasNext()) return false;
        while (iterator.hasNext()) {
            if(!containsKey(iterator.next())) return false;
        }
        return true;
    }

    @Override
    public boolean containsAnyKey(String... names) {
        if(names == null || names.length == 0) return false;
        if(!super.initialized() || super.isEmpty()) return false;
        for (String name : names) {
            if(containsKey(name)) return true;
        }
        return false;
    }

    @Override
    public boolean containsAnyKey(Collection<? extends String> names) {
        if(names == null || names.isEmpty()) return false;
        if(!super.initialized() || super.isEmpty()) return false;
        for (String name : names) {
            if(containsKey(name)) return true;
        }
        return false;
    }


    @Override
    public boolean containsAnyKey(OwnRows<? extends String> names) {
        if(names == null || names.isEmpty()) return false;
        if(super.initialized() && !super.isEmpty()) {
            for (String name : names) if(containsKey(name)) return true;
        }
        return false;
    }


    @Override
    public boolean containsAnyKey(Iterable<? extends String> names) {
        if(names == null) return false;
        Iterator<? extends String> iterator = names.iterator();
        if(!iterator.hasNext()) return false;
        while (iterator.hasNext()) {
            if(containsKey(iterator.next())) return true;
        }
        return false;
    }

    @Override
    public boolean containsValue(Object value) {
        if(Objects.equals(code, value) || Objects.equals(message, value) || Objects.equals(data, value)) return true;
        return super.containsValue(value);
    }

    @Override
    public boolean containsAllValue(Object... values) {
        if(values == null || values.length == 0) return false;
        if(!super.initialized() || super.isEmpty()) return false;
        for (Object value : values) {
            if(!containsValue(value)) return false;
        }
        return true;
    }

    @Override
    public boolean containsAllValue(Collection<?> values) {
        if(values == null || values.isEmpty()) return false;
        if(!super.initialized() || super.isEmpty()) return false;
        for (Object value : values) {
            if(!containsValue(value)) return false;
        }
        return true;
    }


    @Override
    public boolean containsAllValue(OwnRows<?> values) {
        if(values == null || values.isEmpty()) return false;
        if(super.initialized() && !super.isEmpty()) {
            for (Object value : values) {
                if(!containsValue(value)) return false;
            }
            return true;
        }
        return false;
    }


    @Override
    public boolean containsAllValue(Iterable<?> names) {
        if(names == null) return false;
        Iterator<?> iterator = names.iterator();
        if(!iterator.hasNext()) return false;
        while (iterator.hasNext()) {
            if(!containsValue(iterator.next())) return false;
        }
        return true;
    }


    @Override
    public boolean containsAnyValue(Object... values) {
        if(values == null || values.length == 0) return false;
        if(!super.initialized() || super.isEmpty()) return false;
        for (Object value : values) {
            if(containsValue(value)) return true;
        }
        return false;
    }

    @Override
    public boolean containsAnyValue(Collection<?> values) {
        if(values == null || values.isEmpty()) return false;
        if(!super.initialized() || super.isEmpty()) return false;
        for (Object value : values) {
            if(containsValue(value)) return true;
        }
        return false;
    }


    @Override
    public boolean containsAnyValue(OwnRows<?> values) {
        if(values == null || values.isEmpty()) return false;
        if(super.initialized() && !super.isEmpty()) {
            for (Object value : values) if(containsValue(value)) return true;
        }
        return false;
    }


    @Override
    public boolean containsAnyValue(Iterable<?> values) {
        if(values == null) return false;
        Iterator<?> iterator = values.iterator();
        if(!iterator.hasNext()) return false;
        while (iterator.hasNext()) {
            if(containsValue(iterator.next())) return true;
        }
        return false;
    }

    /* *******************************************************************************************
     *
     *          data 操作
     *
     * *******************************************************************************************
     * */

    public JsonPair putInData(String key, Object value) {
        private_castToMap(1).put(key, value);
        return this;
    }

    public JsonPair putAllInData(Map map) {
        if(map == null || map.isEmpty()) return this;
        LinkedHashMap<String, Object> data = private_castToMap(map.size());
        for (Object o : map.entrySet()) {
            Map.Entry e = (Map.Entry) o;
            data.put(ObjKit.asStringValue(e.getKey(), null), e.getValue());
        }
        return this;
    }

    public JsonPair putAllInData(OwnPairs pairs) {
        if(pairs != null && !pairs.isEmpty()) {
            LinkedHashMap<String, Object> data = private_castToMap(pairs.size());
            for (Object o : pairs.entrySet()) {
                Map.Entry e = (Map.Entry) o;
                data.put(ObjKit.asStringValue(e.getKey(), null), e.getValue());
            }
        }
        return this;
    }

    public JsonPair putAllInData(String jsonObject) {
        if(VerifyKit.isJsonObject(jsonObject)) {
            LinkedHashMap<String, Object> map = JsonKit.toEntity(jsonObject, new TypeReference<LinkedHashMap<String, Object>>() {});
            if(map.isEmpty()) return this;
            private_castToMap(map.size()).putAll(map);
        }
        return this;
    }

    public JsonPair putAllInData(Object javaBean) {
        if(javaBean == null) return this;
        Map<String, Object> map = ObjKit.allFieldValue(javaBean);
        if(map.isEmpty()) return this;
        private_castToMap(map.size()).putAll(map);
        return this;
    }


    public JsonPair putIfAbsentInData(String key, Object value) {
        private_castToMap(1).putIfAbsent(key, value);
        return this;
    }

    public JsonPair removeFromData(String key) {
        LinkedHashMap<String, Object> data = private_castToMap();
        if(data != null && !data.isEmpty()) {
            data.remove(key);
        }
        return this;
    }

    public JsonPair removeFromData(String key, Object value) {
        LinkedHashMap<String, Object> data = private_castToMap();
        if(data != null && !data.isEmpty()) {
            data.remove(key, value);
        }
        return this;
    }

    public JsonPair removeFromData(String... keys) {
        if(keys == null || keys.length == 0) return this;
        LinkedHashMap<String, Object> data = private_castToMap();
        if(data == null || data.isEmpty()) return this;
        for (String key : keys) data.remove(key);
        return this;
    }

    public JsonPair removeFromData(Iterable<String> keys) {
        if(keys == null) return this;
        LinkedHashMap<String, Object> data = private_castToMap();
        if(data == null || data.isEmpty()) return this;
        for (String key : keys) data.remove(key);
        return this;
    }

    public JsonPair clearFromData() {
        LinkedHashMap<String, Object> data = private_castToMap();
        if(data != null && !data.isEmpty()) data.clear();
        return this;
    }


    public Object gainFromData(String key) {
        LinkedHashMap<String, Object> data = private_castToMap();
        if(data == null || data.isEmpty()) return null;
        return data.get(key);
    }

    public <T> T gainFromData(String key, Class<T> valueType) {
        return (T) this.gainFromData(key);
    }

    public <T> T gainFromData(String key, C<T> valueType) {
        return (T) ObjKit.toRealValue(this.gainFromData(key), valueType);
    }

    public boolean containsInData(String key) {
        LinkedHashMap<String, Object> data = private_castToMap();
        if(data == null || data.isEmpty()) return false;
        return data.containsKey(key);
    }

    public int dataSize() {
        LinkedHashMap<String, Object> data = private_castToMap();
        return data == null ? 0 : data.size();
    }

    public boolean dataIsEmpty() {
        LinkedHashMap<String, Object> data = private_castToMap();
        return data == null || data.isEmpty();
    }

    public LinkedHashMap<String, Object> dataAsMap() {
        LinkedHashMap<String, Object> data = private_castToMap();
        return data == null ? new LinkedHashMap<>() : data;
    }

    /* *******************************************************************************************
     *
     *          私有逻辑
     *
     * *******************************************************************************************
     * */

    @Note("初始化父类 LinkedHashMap 容器，并往其中 put 初始数据")
    private void private_lazyInitBody(int initSize) {
        super.protected_lazyInitBody(3 + initSize, MapEnum.LINKED);
        super.protected_putOne(CODE, code);
        super.protected_putOne(MESSAGE, message);
        if(data != null) super.protected_putOne(DATA, data);
    }

    @Note("检查要 put 的 name 是否是三大属性")
    private boolean private_addCheck(String name, Object value) {
        if (CODE.equals(name)) {
            this.setCode(BasicEnum.intValue(value));
            return true;
        }
        if (MESSAGE.equals(name)) {
            this.setMessage(ObjKit.toStringEmptyString(value));
            return true;
        }
        if (DATA.equals(name)) {
            this.setData(value);
            return true;
        }
        return false;
    }

    @Note("往父类 LinkedHashMap 容器新增一条 key-value 数据")
    private void private_putOne(String name, Object value) {
        if(private_addCheck(name, value)) return;
        private_lazyInitBody(1);
        super.protected_putOne(name, value);
    }


    @Note("移除父类 LinkedHashMap 容器中的某个 key")
    private boolean private_removeCheck(Object name) {
        if(CODE.equals(name) || MESSAGE.equals(name)) return true;
        if(DATA.equals(name)) {
            this.setData(null);
            super.protected_removeOne(name);
            return true;
        }
        return false;
    }

    @Note("移除父类 LinkedHashMap 容器中的某个 key 和 value（value 要与源值一致）")
    public boolean private_removeCheck(Object name, Object value) {
        if(CODE.equals(name) || MESSAGE.equals(name)) return true;
        if(DATA.equals(name) && Objects.equals(data, value)) {
            this.setData(null);
            super.protected_removeOne(name, value);
            return true;
        }
        return false;
    }

    @Note("如果 data 为 null 则初始化为 LinkedHashMap，然后强转返回")
    private LinkedHashMap<String, Object> private_castToMap(int initSize) {
        if(this.data == null) {
            this.data = new LinkedHashMap<>(MapEnum.calcInitSize(initSize));
        }
        return (LinkedHashMap<String, Object>) this.data;
    }

    @Note("强转 data 为 LinkedHashMap 返回")
    private LinkedHashMap<String, Object> private_castToMap() {
        if(this.data instanceof LinkedHashMap) {
            return (LinkedHashMap<String, Object>) this.data;
        }
        return null;
    }

}
